package org.colafries.admin.service.impl.index;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.colafries.admin.entity.domain.CustomTable;
import org.colafries.admin.entity.domain.index.*;
import org.colafries.admin.entity.vo.ColumnRspVO;
import org.colafries.admin.entity.vo.index.DerivativeIndexDTO;
import org.colafries.admin.entity.vo.index.IndexQueryReqVO;
import org.colafries.admin.entity.vo.index.IndexQueryRspVO;
import org.colafries.admin.entity.vo.index.LogicDimensionDTO;
import org.colafries.admin.exceptions.ServiceException;
import org.colafries.admin.exceptions.enums.ErrorCodeEnum;
import org.colafries.admin.querybuilder.entity.IndexQueryParamContext;
import org.colafries.admin.querybuilder.entity.IndexQuerySqlBuildContext;
import org.colafries.admin.querybuilder.entity.SelectSqlSchema;
import org.colafries.admin.querybuilder.indexQuery.DerivativeIndexBuilder;
import org.colafries.admin.querybuilder.indexQuery.DimensionBuilder;
import org.colafries.admin.querybuilder.indexQuery.IndexQuerySqlSegmentBuilder;
import org.colafries.admin.querybuilder.utils.SqlSegmentBuilderUtil;
import org.colafries.admin.service.IDataBaseService;
import org.colafries.admin.service.index.*;
import org.colafries.common.utils.HashUtil;
import org.colafries.metadata.result.JdbcSelectResult;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author Ann
 * @version v1.0
 * @ClassName IQueryServiceImpl
 * @Description
 * @date Created in 2024/6/13
 **/
@Service
@Slf4j
public class IndexQueryServiceImpl implements IndexQueryService {
    @Resource
    IDerivativeIndexService iDerivativeIndexService;
    @Resource
    IAtomicMetricService iAtomicMetricService;
    @Resource
    ILogicConditionService iLogicConditionService;
    @Resource
    IDerivativeIdxCondRelService iDerivativeIdxCondRelService;
    @Resource
    ILogicFactTableFieldService iLogicFactTableFieldService;
    @Resource
    ILogicFactTableService iLogicFactTableService;
    @Resource
    ILogicDimensionService iLogicDimensionService;
    @Resource
    ILogicDimRelationService iLogicDimRelationService;
    @Resource
    IDataBaseService iDataBaseService;

    /**
     * 指标查询
     *
     * @param vo
     * @return org.colafries.admin.entity.vo.index.IndexQueryRspVO
     * @Author anning
     * @Date 2024/6/20
     */
    @Override
    public IndexQueryRspVO query(IndexQueryReqVO vo) {
        // 构建查询参数Context
        IndexQueryParamContext queryParamContext = buildQueryParamContext(vo);
        // 构建查询SQL
        String querySql = buildQuerySQL(queryParamContext);
        // 如果自定义查询参数中指定了limit，则按照limit返回数据
        JdbcSelectResult jdbcSelectResult = iDataBaseService.querySqlPage(
                queryParamContext.getFactTable().getSourceDbId(),
                querySql, vo.getCurrent(), vo.getSize()
        );

        IndexQueryRspVO indexQueryRspVO = new IndexQueryRspVO();
        List<ColumnRspVO> columnRspVOs = buildColumnRspVOList(jdbcSelectResult, queryParamContext);
        indexQueryRspVO.setQuerySql(querySql)
                .setColumns(columnRspVOs)
                .setRows(jdbcSelectResult.getRowData())
                .setExecTime(jdbcSelectResult.getExecTime())
                .setCurrent(vo.getCurrent())
                .setSize(vo.getSize())
                .setTotal(jdbcSelectResult.getTotal());
        return indexQueryRspVO;
    }

    /**
     * 构建查询结果列集合
     *
     * @return java.util.List<org.colafries.admin.entity.vo.ColumnRspVO>
     * @Author anning
     * @Date 2024/6/28
     */
    public List<ColumnRspVO> buildColumnRspVOList(JdbcSelectResult jdbcSelectResult, IndexQueryParamContext queryParamContext) {
        return jdbcSelectResult.getColumns().stream()
                .map(column -> ColumnRspVO.builder()
                        .field(column.getName())
                        .dbType(column.getType())
                        .javaType(column.getJavaTypeFormat())
                        .comment(column.getComment())
                        .fieldHash(HashUtil.genFieldHash("" + "." + column.getName()))
                        .build())
                .collect(Collectors.toList());
    }

    /**
     * 构建查询参数
     *
     * @param vo
     * @return org.colafries.admin.querybuilder.entity.IndexQueryParamContext
     * @Author anning
     * @Date 2024/6/20
     */
    public IndexQueryParamContext buildQueryParamContext(IndexQueryReqVO vo) {
        IndexQueryParamContext queryParamContext = new IndexQueryParamContext();
        // 派生指标构建参数
        try {
            vo.getIndexCodeList().forEach(indexCode -> {
                buildDerivativeIndexDTO(indexCode, queryParamContext);
            });
        } catch (Exception e) {
            log.warn("构建派生指标查询参数失败:{}", e.getMessage());
            throw new ServiceException(ErrorCodeEnum.GL999999, "构建派生指标查询参数失败");
        }
        // 逻辑维度参数构建
        try {
            vo.getDimensionCodeList().forEach(dimensionCode -> {
                buildDimensionDTO(dimensionCode, queryParamContext);
            });
        } catch (Exception e) {
            log.warn("根据逻辑维度参数构建异常:{}", e.getMessage());
            throw new ServiceException(ErrorCodeEnum.GL999999, "根据逻辑维度参数构建异常");
        }

        // 1.获取去重后的fieldCodeList
        // 2.根据fieldCodeList转换成逻辑表字段Map
        // 3.设置 queryParamContext.logicTableFieldMap
        queryParamContext.getFactTableFieldMap().putAll(convertCodesToTableFieldMap(queryParamContext.getUniqueFactTableFieldCodeList()));

        List<IndexQuerySqlSegmentBuilder> builders = Arrays.asList(
                new DerivativeIndexBuilder(),
                new DimensionBuilder()
        );
        // build Query
        builders.forEach(builder -> builder.buildQueryParma(queryParamContext));

        return queryParamContext;
    }

    /**
     * 构建指标查询SQL
     *
     * @return java.lang.String
     * @Author anning
     * @Date 2024/6/19
     */
    public String buildQuerySQL(IndexQueryParamContext queryParamContext) {
        IndexQuerySqlBuildContext sqlBuildContext = new IndexQuerySqlBuildContext();
        List<IndexQuerySqlSegmentBuilder> builders = Arrays.asList(
                new DerivativeIndexBuilder(),
                new DimensionBuilder()
        );
        // build Query
        builders.forEach(builder -> builder.buildQuery(queryParamContext, sqlBuildContext));
        // build Custom tables
        List<CustomTable> customTables = buildCustomTableList(queryParamContext);
        SelectSqlSchema sqlSchema = SqlSegmentBuilderUtil.buildSelectSqlSchema(sqlBuildContext, customTables);
        return SqlSegmentBuilderUtil.buildFinalSql(sqlSchema);
    }

    /**
     * 构建派生指标DTO
     *
     * @param derivativeIndexCode
     * @param queryParamContext
     * @return void
     * @Author anning
     * @Date 2024/6/25
     */
    public void buildDerivativeIndexDTO(Long derivativeIndexCode, IndexQueryParamContext queryParamContext) {
        // DerivativeIndexDTO
        DerivativeIndex derivativeIndex = iDerivativeIndexService.queryByCode(derivativeIndexCode);
        if (derivativeIndex == null) {
            log.warn("派生指标不存在 indexCode:{}", derivativeIndexCode);
            throw new ServiceException(ErrorCodeEnum.GL999999, "派生指标不存在");
        }
        // build原子指标DTO
        Long atomicMetricCode = derivativeIndex.getAtomicMetricCode();
        AtomicMetric atomicMetric = iAtomicMetricService.queryByCode(atomicMetricCode);
        if (atomicMetric == null) {
            log.warn("原子指标不存在 atomicMetricCode{}", atomicMetricCode);
            throw new ServiceException(ErrorCodeEnum.GL999999, "原子指标不存在");
        }
        // 转换表达式，构建atomicMetricDTO
        DerivativeIndexDTO.AtomicMetricDTO atomicMetricDTO = DerivativeIndexDTO.AtomicMetricDTO.builder()
                .code(atomicMetric.getCode())
                .expression(atomicMetric.getExpression())
                .build();
        // 获取表达式中的fieldCodeList
        List<String> atomicMetricFieldCodeList = SqlSegmentBuilderUtil.getExpKeyList(atomicMetric.getExpression());
        queryParamContext.getFactTableFieldCodeList().addAll(atomicMetricFieldCodeList.stream().map(Long::parseLong).collect(Collectors.toList()));
        List<DerivativeIdxConditionRel> conditionRelations = iDerivativeIdxCondRelService.listByDerivativeCode(derivativeIndex.getCode());
        if (conditionRelations == null) {
            log.warn("派生指标与业务限定关系不存在，派生指标code:{}", derivativeIndex.getCode());
            throw new ServiceException(ErrorCodeEnum.GL999999, "派生指标与业务限定关系不存在");
        }
        List<DerivativeIndexDTO.LogicConditionDTO> conditionDTOList = Lists.newArrayList();
        conditionRelations.forEach(conditionRelation -> {
            LogicCondition condition = iLogicConditionService.queryByCode(conditionRelation.getConditionCode());
            if (condition == null) {
                log.warn("业务限定不存在 conditionCode:{}", conditionRelation.getConditionCode());
                throw new ServiceException(ErrorCodeEnum.GL999999, "业务限定不存在");
            }
            conditionDTOList.add(DerivativeIndexDTO.LogicConditionDTO.builder()
                    .code(condition.getCode())
                    .expression(condition.getExpression())
                    .build());
            // 设置 queryParamContext.conditionMap
            queryParamContext.getConditionMap().put(condition.getCode(), condition);
            // 设置queryParamContext.tableFieldCodeList
            List<String> conditionFieldCodeList = SqlSegmentBuilderUtil.getExpKeyList(condition.getExpression());
            queryParamContext.getFactTableFieldCodeList().addAll(conditionFieldCodeList.stream().map(Long::parseLong).collect(Collectors.toList()));
        });

        // 构建派生指标DTO
        DerivativeIndexDTO derivativeIndexDTO = new DerivativeIndexDTO();
        derivativeIndexDTO.setDerivativeIndexCode(derivativeIndex.getCode())
                .setField(derivativeIndex.getField())
                .setAlias(derivativeIndex.getAlias())
                .setAtomicMetricDTO(atomicMetricDTO)
                .setConditionDTOList(conditionDTOList);
        // 设置queryParamContext.derivativeIndexDTOList
        queryParamContext.getDerivativeIndexDTOList().add(derivativeIndexDTO);

        // 根据派生指标解析的factTableFieldCode查询factTable
        LogicFactTable factTable = queryParamContext.getFactTableFieldCodeList().stream().findFirst().map(factTableFieldCode -> {
            LogicFactTableField factTableField = iLogicFactTableFieldService.queryByCode(factTableFieldCode);
            if (factTableField == null) {
                log.warn("逻辑事实表字段不存在 fileCode {}", factTableFieldCode);
                throw new ServiceException(ErrorCodeEnum.GL999999, "逻辑事实表字段不存在");
            }
            return iLogicFactTableService.queryByCode(factTableField.getLogicTableCode());
        }).orElse(null);
        queryParamContext.setFactTable(factTable);

    }

    /**
     * 构建逻辑维度DTO
     *
     * @param dimensionCode
     * @param queryParamContext
     * @return void
     * @Author anning
     * @Date 2024/6/25
     */
    public void buildDimensionDTO(Long dimensionCode, IndexQueryParamContext queryParamContext) {
        LogicDimension dimension = iLogicDimensionService.queryByCode(dimensionCode);
        if (dimension == null) {
            log.warn("逻辑维度不存在 dimensionCode{}", dimensionCode);
            throw new ServiceException(ErrorCodeEnum.GL999999, "逻辑维度不存在");
        }
        LogicDimensionDTO dimensionDTO = new LogicDimensionDTO();
        dimensionDTO.setDimensionCode(dimensionCode);
        dimensionDTO.setField(dimension.getField());
        List<LogicDimRelation> logicDimRelations = iLogicDimRelationService.listByDimensionCode(dimensionCode);
        if (CollectionUtils.isEmpty(logicDimRelations)) {
            log.warn("逻辑维度与逻辑事实表字段关系不存在 dimensionCode:{}", dimensionCode);
            throw new ServiceException(ErrorCodeEnum.GL999999, "逻辑维度与逻辑事实表字段关系不存在");
        }
        logicDimRelations.forEach(logicDimRelation -> {
            String expression = logicDimRelation.getExpression();
            // 从表达式中解析所有的fieldCode
            List<String> parsedFieldCodes = SqlSegmentBuilderUtil.getExpKeyList(expression);
            queryParamContext.getFactTableFieldCodeList().addAll(parsedFieldCodes.stream().map(Long::parseLong).collect(Collectors.toList()));
            dimensionDTO.getFactTableFieldCodeList().addAll(parsedFieldCodes.stream().map(Long::parseLong).collect(Collectors.toList()));
        });
        queryParamContext.getDimensionDTOList().add(dimensionDTO);
    }

    /**
     * 构建指标自定义查询Tables
     * 原子指标字段下推
     *
     * @param queryParamContext
     * @return java.util.List<org.colafries.admin.entity.domain.CustomTable>
     * @Author anning
     * @Date 2024/6/21
     */
    public List<CustomTable> buildCustomTableList(IndexQueryParamContext queryParamContext) {
        List<CustomTable> customTableList = Lists.newArrayList();
        LogicFactTable logicFactTable = iLogicFactTableService.queryByCode(queryParamContext.getFactTable().getCode());
        List<LogicFactTableField> fieldList = new ArrayList<>(queryParamContext.getFactTableFieldMap().values());
        //
        String selectExpression = fieldList.stream()
                .map(factTableField -> {
                    if (SqlSegmentBuilderUtil.getFunctionExpFlag(factTableField.getExpression())) {
                        return String.format("%s AS %s", factTableField.getExpression(), factTableField.getField());
                    }
                    return factTableField.getExpression();
                })
                .collect(Collectors.joining(","));
        String tableExpression = logicFactTable.getSourceSchema() + "." + logicFactTable.getSourceTableName();
        String whereExpression = logicFactTable.getExpression();
        String customSql = String.format("SELECT %s FROM %s WHERE %s", selectExpression, tableExpression, whereExpression);

        CustomTable customTable = new CustomTable();
        customTable.setCustomSql(customSql).setTableHash(HashUtil.genCustomTableHash(customSql));
        customTableList.add(customTable);
        return customTableList;
    }

    /**
     * 将逻辑表字段codeList转换成逻辑表字段Map
     *
     * @param fieldCodeList
     * @return java.util.Map<java.lang.Long, org.colafries.admin.entity.domain.index.LogicFactTableField>
     * @Author anning
     * @Date 2024/6/26
     */
    public Map<Long, LogicFactTableField> convertCodesToTableFieldMap(List<Long> fieldCodeList) {
        // 逻辑字段缓存区
        Map<Long, LogicFactTableField> fieldHashMap = Maps.newHashMap();

        return fieldCodeList.stream().map(fieldCode -> {
            if (fieldHashMap.containsKey(fieldCode)) {
                return fieldHashMap.get(fieldCode);
            }
            // 如果缓存中找不到fieldCode，则查询数据库
            LogicFactTableField logicFactTableField = iLogicFactTableFieldService.queryByCode(fieldCode);
            if (logicFactTableField == null) {
                log.warn("逻辑表字段不存在 fieldCode:{}", fieldCode);
                throw new ServiceException(ErrorCodeEnum.GL999999, "逻辑表字段不存在");
            }
            List<LogicFactTableField> fieldList = iLogicFactTableFieldService.listByTableCode(logicFactTableField.getLogicTableCode());
            // 将逻辑表对应的全部字段添加到缓存，减少数据库查询
            fieldHashMap.putAll(fieldList.stream().collect(Collectors.toMap(LogicFactTableField::getCode, Function.identity())));
            return logicFactTableField;
        }).collect(Collectors.toMap(LogicFactTableField::getCode, Function.identity()));
    }


    /**
     * 请求参数验证
     *
     * @param vo
     * @return void
     * @Author anning
     * @Date 2024/6/19
     */
    public void validateReqVo(IndexQueryReqVO vo) {

    }
}
